providers.tsx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. "use client";
  2. import Sidebar from "@/components/Layout/Sidebar";
  3. import { useSystemStore } from "@/stores/useSystemStore";
  4. import { ConfigProvider, Mask } from "antd-mobile";
  5. import ptBR from "antd-mobile/es/locales/pt-BR";
  6. import { ThemeProviderProps } from "next-themes/dist/types";
  7. import { ReactNode, useEffect, useMemo, useState } from "react";
  8. import { server } from "@/utils/client";
  9. import { useDebounceEffect, useRequest } from "ahooks";
  10. import clsx from "clsx";
  11. import { initializeApp } from "firebase/app";
  12. import { getMessaging, getToken, onMessage } from "firebase/messaging";
  13. import { usePathname, useSearchParams } from "next/navigation";
  14. import Script from "next/script";
  15. import { setupFontSize } from "@/utils";
  16. import { Local } from "@/utils/storage";
  17. import { motion } from "framer-motion";
  18. import Image from "next/image";
  19. import styles from "./style.module.scss";
  20. export interface ProvidersProps {
  21. children: ReactNode;
  22. themeProps?: Omit<ThemeProviderProps, "children">;
  23. }
  24. // 初始化 fireBase
  25. const initFirebase = () => {
  26. // 是否是https
  27. if (document.location.protocol.indexOf("https") === -1) return;
  28. // 浏览器是否支持 且是 pwa
  29. if (!window.Notification) {
  30. return;
  31. }
  32. // 是否开启通知
  33. // new Notification("这是标题", {
  34. // body: "这是正文",
  35. // icon: "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
  36. // requireInteraction: true,
  37. // image: "https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png",
  38. // });
  39. if (Notification.permission === "default") {
  40. // 征求用户的许可
  41. Notification.requestPermission();
  42. }
  43. if (Notification.permission === "denied") return;
  44. const app = initializeApp({
  45. apiKey: process.env.NEXT_PUBLIC_FIREBASE_APIKEY,
  46. authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTHDOMAIN,
  47. projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECTID,
  48. storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGEBUCKET,
  49. messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGINGSENDERID,
  50. appId: process.env.NEXT_PUBLIC_FIREBASE_APPID,
  51. measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENTID,
  52. });
  53. const messaging = getMessaging(app);
  54. // 针对单机测试,或者服务端需要使用这个key可以放开
  55. // if (process.env.NODE_ENV === "development") {
  56. getToken(messaging, {
  57. vapidKey: process.env.NEXT_PUBLIC_FIREBASE_KEYS,
  58. }).then((res) => {});
  59. // }
  60. onMessage(messaging, (payload) => {
  61. const notifica = new Notification(payload.data?.title || "", {
  62. body: payload.data?.body,
  63. icon: payload.data?.image,
  64. ...payload.data,
  65. });
  66. });
  67. };
  68. /**
  69. * 获取停服公告信息
  70. * POST /v1/api/front/suspension
  71. * 接口ID:263127760
  72. * 接口地址:https://app.apifox.com/link/project/4790544/apis/api-263127760
  73. */
  74. interface SuspensionType {
  75. able: boolean;
  76. }
  77. const getStopServiceApi = () => {
  78. return server
  79. .post<SuspensionType>({
  80. url: "/v1/api/front/suspension",
  81. data: { renter_id: "10000" },
  82. })
  83. .then((res) => {
  84. return res.data.able ?? false;
  85. });
  86. };
  87. /**
  88. * @description 停服通知
  89. */
  90. const StopServiceClient = () => {
  91. const { data: isSuspension } = useRequest(getStopServiceApi, {
  92. pollingInterval: 10000,
  93. pollingErrorRetryCount: 3,
  94. });
  95. return (
  96. <>
  97. {isSuspension && (
  98. <div
  99. className={
  100. "absolute left-0 right-0 top-[0.4rem] z-[99999] bg-[#fff] p-[20px]" +
  101. " text-[14px] text-[#000]"
  102. }
  103. >
  104. <div className={"flex items-center"}>
  105. <div>
  106. <motion.div
  107. animate={{ opacity: [0, 100, 0] }}
  108. transition={{ repeat: Infinity, repeatDelay: 1 }}
  109. >
  110. <span
  111. className={
  112. "iconfont icon-gantanhao mr-[10px] text-primary-color" +
  113. " text-[0.2rem]"
  114. }
  115. ></span>
  116. </motion.div>
  117. </div>
  118. <span>
  119. O site está prestes a parar de ser atualizado, a fim de proteger a
  120. segurança de seus fundos, temporariamente impossibilitado de fazer
  121. apostas
  122. </span>
  123. </div>
  124. </div>
  125. )}
  126. </>
  127. );
  128. };
  129. /**
  130. * @description 全局弹窗
  131. */
  132. const GlobalNotify = () => {
  133. const [visible, setVisible] = useState(false);
  134. return (
  135. <>
  136. <Mask visible={visible} onMaskClick={() => setVisible(false)}>
  137. <div className="absolute left-0 top-[22%] w-[100%]">
  138. <div className={"relative h-[2.6rem]"}>
  139. {/*内容*/}
  140. <div
  141. className={"absolute left-0 top-0 z-10 h-[100%] w-[100%] p-[0.4167rem]"}
  142. >
  143. <div className={"flex h-[100%] flex-col items-center justify-center"}>
  144. <p className={"text-[0.2083rem] font-semibold text-[#532e0a]"}>
  145. GANHE UM BÔNUS DE
  146. </p>
  147. <p
  148. className={
  149. "text-[0.5rem] font-black leading-[0.5rem]" +
  150. " text-[#ec5c52]"
  151. }
  152. >
  153. 999.00
  154. </p>
  155. </div>
  156. </div>
  157. {/*title*/}
  158. <Image
  159. src={"/notify/title.png"}
  160. className={
  161. "absolute -top-[0.7rem] left-1/2 -translate-x-1/2" +
  162. " w-[2.7778rem]"
  163. }
  164. alt={""}
  165. width={400}
  166. height={70}
  167. />
  168. {/*铃铛*/}
  169. <Image
  170. src={"/notify/bell.png"}
  171. className={
  172. "absolute -top-[0.1rem] left-1/2 w-[0.8333rem] -translate-x-1/2"
  173. }
  174. alt={""}
  175. width={120}
  176. height={70}
  177. />
  178. {/*金币*/}
  179. <Image
  180. src={"/notify/money.png"}
  181. alt={""}
  182. width={750}
  183. className={
  184. "absolute -top-[0.5rem] left-0 h-[3.2rem] animate-slow-bounce"
  185. }
  186. height={512}
  187. />
  188. {/*光*/}
  189. <Image
  190. src={"/notify/light.png"}
  191. alt={""}
  192. width={750}
  193. className={
  194. "absolute -top-[120px] left-0 -z-10 h-[4.2rem] animate-spin" +
  195. " border-[green]"
  196. }
  197. style={{ animationDuration: "4s" }}
  198. height={512}
  199. />
  200. <Image
  201. src={"/notify/light1.png"}
  202. alt={""}
  203. width={750}
  204. className={"absolute -top-[80px] left-0 -z-10 h-[3.2rem]"}
  205. height={512}
  206. />
  207. {/*背景*/}
  208. <Image
  209. src={"/notify/bg.png"}
  210. alt={""}
  211. width={750}
  212. height={424}
  213. className={"mx-auto h-[2.6rem] w-[90%]"}
  214. />
  215. </div>
  216. </div>
  217. </Mask>
  218. </>
  219. );
  220. };
  221. /**
  222. * @description 初始化pixel广告追踪
  223. *
  224. * 1:获取url参数
  225. * - 如果 有url参数获取并存入本地缓存
  226. * - 如果 没有url参数获取本地缓存
  227. * 2:根据类型获取对应的id
  228. */
  229. //bcwin?ch=ugZ8z9mf8x&type=kwai_pixel&kwaiPixel=2222 fbPixel kwaiPixel
  230. // bcwin11111?ch=TcC4Wno4Cq&type=facebook_pixel&fbPixel=625811426763645 // kwai 271573370973328
  231. /**
  232. * @description type
  233. * 1: kwai_pixel
  234. * 2: facebook_pixel
  235. */
  236. const InitAdvertise = () => {
  237. const pathname = useSearchParams();
  238. // pixel 类型
  239. const [pixelType, setPixelType] = useState<string | null>(null);
  240. const [pixelId, setPixelId] = useState<string | null>(null);
  241. // 快玩id
  242. const kwaiPixel = pathname.get("kwaiPixel");
  243. const kwaiClick_id = pathname.get("click_id");
  244. // facebook id
  245. const facebookPixel = pathname.get("fbPixel");
  246. // console.log(
  247. // `🚀🚀🚀🚀🚀-> in providers.tsx on 152`,
  248. // typeof window,
  249. // typeof window !== undefined ? window.localStorage.getItem("pixel_type") : ""
  250. // );
  251. useEffect(() => {
  252. if (kwaiPixel) {
  253. setPixelType("kwai_pixel");
  254. Local.setKey("ban_pixel_type", "kwai_pixel");
  255. Local.setKey("ban_pixel_id", kwaiPixel);
  256. } else if (facebookPixel) {
  257. setPixelType("facebook_pixel");
  258. Local.setKey("ban_pixel_type", "facebook_pixel");
  259. Local.setKey("ban_pixel_id", facebookPixel);
  260. } else {
  261. const type = Local.getKey("ban_pixel_type");
  262. setPixelType(type);
  263. }
  264. if (kwaiClick_id) {
  265. Local.setKey("ban_click_id", kwaiClick_id);
  266. }
  267. setPixelId(kwaiPixel || facebookPixel || Local.getKey("ban_pixel_id"));
  268. }, []);
  269. return (
  270. <>
  271. {pixelType === "facebook_pixel" && (
  272. <Script
  273. id={"facebook"}
  274. strategy={"afterInteractive"}
  275. dangerouslySetInnerHTML={{
  276. __html: `
  277. fbq('init', '${pixelId}');
  278. `,
  279. }}
  280. ></Script>
  281. )}
  282. {pixelType === "kwai_pixel" && (
  283. <Script
  284. id={"kwai"}
  285. strategy={"afterInteractive"}
  286. dangerouslySetInnerHTML={{
  287. __html: `
  288. kwaiq.load('${pixelId}');
  289. kwaiq.page();
  290. `,
  291. }}
  292. ></Script>
  293. )}
  294. </>
  295. );
  296. };
  297. export default function SidebarLayout({ children, themeProps }: ProvidersProps) {
  298. const { isCollapse, setCollapse } = useSystemStore((state) => ({
  299. isCollapse: state.isCollapse,
  300. setCollapse: state.setCollapse,
  301. }));
  302. const pathname = usePathname();
  303. const isShowBg = useMemo(() => {
  304. const local = pathname.split("/")[1];
  305. if (`/${local}` === pathname) return true;
  306. return [
  307. `/${local}/freeGames`,
  308. `/${local}/replayGames`,
  309. `/${local}/promo`,
  310. `/${local}/gameList`,
  311. ].includes(pathname);
  312. }, [pathname]);
  313. return (
  314. <div id={"app"} className="relative h-[100%] overflow-hidden">
  315. <motion.div
  316. className="fixed left-0 top-0 z-30 h-[100%] w-[70%]"
  317. initial={{ x: "-100%" }}
  318. animate={{ x: isCollapse ? 0 : "-100%" }}
  319. transition={{ type: "tween", duration: 0.3 }}
  320. >
  321. <Sidebar />
  322. </motion.div>
  323. {/* Main Content transform: translate(0, 0);*/}
  324. <motion.div
  325. className="relative z-10 h-[100%] translate-x-0 translate-y-0 transform"
  326. animate={{ x: isCollapse ? "70%" : "0%" }}
  327. transition={{ type: "tween", duration: 0.3 }}
  328. >
  329. <div
  330. className={clsx(isCollapse ? "containerMask" : "")}
  331. onClick={() => {
  332. setCollapse(false);
  333. }}
  334. ></div>
  335. {/*停服通知*/}
  336. <StopServiceClient />
  337. {/*充值成功通知*/}
  338. <GlobalNotify />
  339. <section className={clsx(isShowBg && styles.homePage, "relative h-[100%]")}>
  340. {children}
  341. </section>
  342. </motion.div>
  343. </div>
  344. );
  345. }
  346. export const Providers = ({ children, themeProps }: ProvidersProps) => {
  347. const setupConfig = useSystemStore((state) => state.setupConfig);
  348. useDebounceEffect(() => {
  349. if ("serviceWorker" in navigator) {
  350. initFirebase();
  351. }
  352. // 初始化配置
  353. setupConfig();
  354. // 初始化字体
  355. setupFontSize();
  356. }, []);
  357. return (
  358. <ConfigProvider locale={ptBR}>
  359. <Script
  360. id={"facebookScript"}
  361. strategy={"beforeInteractive"}
  362. dangerouslySetInnerHTML={{
  363. __html: `<!-- Meta Pixel Code -->
  364. !function(f,b,e,v,n,t,s)
  365. {if(f.fbq)return;n=f.fbq=function(){n.callMethod?
  366. n.callMethod.apply(n,arguments):n.queue.push(arguments)};
  367. if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
  368. n.queue=[];t=b.createElement(e);t.async=!0;
  369. t.src=v;s=b.getElementsByTagName(e)[0];
  370. s.parentNode.insertBefore(t,s)}(window, document,'script',
  371. 'https://connect.facebook.net/en_US/fbevents.js');
  372. <!-- End Meta Pixel Code -->`,
  373. }}
  374. ></Script>
  375. <Script
  376. id={"kwaiScript"}
  377. strategy={"beforeInteractive"}
  378. dangerouslySetInnerHTML={{
  379. __html: `
  380. !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.install=t():e.install=t()}(window,(function(){return function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";var o=this&&this.__spreadArray||function(e,t,n){if(n||2===arguments.length)for(var o,r=0,i=t.length;r<i;r++)!o&&r in t||(o||(o=Array.prototype.slice.call(t,0,r)),o[r]=t[r]);return e.concat(o||Array.prototype.slice.call(t))};Object.defineProperty(t,"__esModule",{value:!0});var r=function(e,t,n){var o,i=e.createElement("script");i.type="text/javascript",i.async=!0,i.src=t,n&&(i.onerror=function(){r(e,n)});var a=e.getElementsByTagName("script")[0];null===(o=a.parentNode)||void 0===o||o.insertBefore(i,a)};!function(e,t,n){e.KwaiAnalyticsObject=n;var i=e[n]=e[n]||[];i.methods=["page","track","identify","instances","debug","on","off","once","ready","alias","group","enableCookie","disableCookie"];var a=function(e,t){e[t]=function(){for(var n=[],r=0;r<arguments.length;r++)n[r]=arguments[r];var i=o([t],n,!0);e.push(i)}};i.methods.forEach((function(e){a(i,e)})),i.instance=function(e){var t,n=(null===(t=i._i)||void 0===t?void 0:t[e])||[];return i.methods.forEach((function(e){a(n,e)})),n},i.load=function(e,o){var a="https://s1.kwai.net/kos/s101/nlav11187/pixel/events.js";i._i=i._i||{},i._i[e]=[],i._i[e]._u=a,i._t=i._t||{},i._t[e]=+new Date,i._o=i._o||{},i._o[e]=o||{};var c="?sdkid=".concat(e,"&lib=").concat(n);r(t,a+c,"https://s16-11187.ap4r.com/kos/s101/nlav11187/pixel/events.js"+c)}}(window,document,"kwaiq")}])}));
  381. `,
  382. }}
  383. ></Script>
  384. <InitAdvertise />
  385. <SidebarLayout>{children}</SidebarLayout>
  386. </ConfigProvider>
  387. );
  388. };